#!/usr/bin/python
import os, sys, time, math, zestlib as zest
################################################################################
################################################################################
def PrintHelpMsgAndDie():
    ( p, cmd ) = os.path.split( sys.argv[0] )
    print 'usage:', cmd, '[-a|--accu]'
    print '... -a or --accu: accurate dedup (4x slower than normal)'
    print ''
    sys.exit( -1 )

def ParseArgv():
    fastdedup = True
    if len( sys.argv ) > 1:
        try:
            assert sys.argv[1] in [ '-a', '--accu' ]
            fastdedup = False
        except:
            PrintHelpMsgAndDie()
    return fastdedup

################################################################################
################################################################################
def ListSubdirs( p ):
    files = os.listdir( p )
    subdirs = [ f for f in files if os.path.isdir( os.path.join( p, f ) ) ]
    return subdirs

def ValidTimeStamp( tsstr ):
    # returns True if tsstr can be parsed into a 'valid' time stamp
    toks = tsstr.split( '__' )
    if len( toks ) != 2: return False
    ymd = toks[0].split( '_' )
    hms = toks[1].split( '_' )
    if len( ymd ) != 3: return False
    if len( hms ) != 3: return False
    try:
        y = int( ymd[0] )
        m = int( ymd[1] )
        d = int( ymd[2] )
        assert m > 0
        assert m < 13
        assert y > 0
        assert d > 0
        assert d < 32
        h = int( hms[0] )
        m = int( hms[1] )
        s = int( hms[2] )
        assert h >= 0
        assert h <= 23
        assert m >= 0
        assert m <= 59
        assert s >= 0
        assert s <= 59
    except:
        return False
    return True

def FindZestDirs( dirs ):
    zestdirs = []
    for d in dirs:
        toks = d.split( '-' )
        if len( toks ) == 2:
            if toks[0] == 'zest':
                if ValidTimeStamp( toks[1] ):
                    zestdirs.append( d )
    return zestdirs

def FindMostRecentImagePath():
    rawimagepath = zest.RawImagePath()
    assert os.path.isdir( rawimagepath )
    subdirs  = ListSubdirs( rawimagepath )
    zestdirs = FindZestDirs( subdirs )
    if not len( zestdirs ):
        return rawimagepath
    recent   = max( zestdirs )
    return os.path.join( rawimagepath, recent )

def IsMemImageFileName( toks ):
    if len( toks ) == 3:
        if toks[0] == 'memimage':
            if ValidTimeStamp( toks[1] ):
                return True
    return False

def MemImgRoot( p, toks ):
    return os.path.join( p, toks[0] + '.' + toks[1] )

def ListMemImages( p ):
    memimages = []
    files = os.listdir( p )
    for f in files:
        pathfile = os.path.join( p, f )
        toks = f.split( '.' )
        if IsMemImageFileName( toks ):
            isfile = os.path.isfile( pathfile )
            ispath = os.path.isdir( pathfile )
            if isfile and not ispath:
                mir = MemImgRoot( p, toks )
                if mir not in memimages:
                    memimages.append( mir )
    return memimages

def CountMemImageChunks( imgFileRoot ):
    count = 0
    ( p, r ) = os.path.split( imgFileRoot )
    assert os.path.isdir( p )
    files = os.listdir( p )
    for f in files:
        toks = f.split( '.' )
        if IsMemImageFileName( toks ):
            if MemImgRoot( p, toks ) == imgFileRoot:
                count += 1
    assert count
    return count

################################################################################
################################################################################
def SanityChecks( zestbin, memimages ):
    if not os.path.isfile( zestbin ):
        print 'cannot find the zest binary'
        print 'run build.py perhaps?'
        sys.exit( -1 )
    
    if not len( memimages ):
        print 'cannot find any memory images to analyze'
        print 'run capture.py perhaps?'
        sys.exit( -1 )

################################################################################
################################################################################
fastdedup = ParseArgv()
zestroot  = zest.ZestRoot()
imagepath = FindMostRecentImagePath()
memimages = ListMemImages( imagepath )
zestbin   = os.path.join( zestroot, 'zest', 'bin', 'zest' )

# setup the log file:
logfile = '.'.join([ 'zest', 'analyze', zest.FileNameTimeStamp(), 'log' ])
logfile = os.path.join( imagepath, logfile )
zest.opentee( logfile )

# some simple sanity checks:
SanityChecks( zestbin, memimages )

# informational printouts:
print 'mem image path:', imagepath
print 'memory images:'
print '\n'.join( memimages )
print 'logfile:', logfile
print ''

numchunk  = CountMemImageChunks( memimages[0] )

# the main loop: run zest on each memory image
for memimage in memimages:
    pagerefs = memimage.replace( 'memimage', 'pagerefs' )
    numchunk = CountMemImageChunks( memimage )
    numthread = os.sysconf( 'SC_NPROCESSORS_ONLN' )
    numthread = int( max( 1, numthread / 2 ) )
    numthread = min( numthread, numchunk )
    if fastdedup: cmd = ' '.join([ zestbin, '-i', memimage, pagerefs, str( numchunk ), str( numthread ), '-f' ])
    else:         cmd = ' '.join([ zestbin, '-i', memimage, pagerefs, str( numchunk ), str( numthread ), '-a' ])
    zest.SystemWrapper( cmd, verbose = True, trial = False )

print ''
print 'logfile:', logfile

